package hr.fer.ztel.rassus.brbljaonica.business;

import hr.fer.ztel.rassus.brbljaonica.gui.IndividualChatWindow;
import hr.fer.ztel.rassus.brbljaonica.gui.MainAppPanel;
import hr.fer.ztel.rassus.brbljaonica.gui.RoomChatWindow;

import java.io.File;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;

import javax.swing.DefaultListModel;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.filetransfer.FileTransferListener;
import org.jivesoftware.smackx.filetransfer.FileTransferManager;
import org.jivesoftware.smackx.filetransfer.FileTransferNegotiator;
import org.jivesoftware.smackx.filetransfer.FileTransferRequest;
import org.jivesoftware.smackx.filetransfer.IncomingFileTransfer;
import org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer;
import org.jivesoftware.smackx.muc.HostedRoom;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.muc.RoomInfo;
import org.jivesoftware.smackx.packet.DiscoverItems.Item;

/**
 * Staticka klasa koja predstavlja sucelje za komunikaciju sa<br>
 * posluziteljem i drugim korisnicima te za postavljanje <br>
 * pogona za njihovo ostvarenje
 * 
 */
public class BusinessProvider {

	/**
	 * Konekcija sa posluziteljem
	 */
	private static Connection connection;
	private static Map<String, IndividualChatWindow> chatWindows = new TreeMap<String, IndividualChatWindow>();
	private static FileTransferManager ftm;

	private BusinessProvider() {
	}

	public static void addChatWindow(String user, IndividualChatWindow chatWindow) {
		chatWindows.put(user, chatWindow);
	}
	
	/**
	 * Metoda za otvaranje veze sa posluziteljem te login na isti
	 * @param username korisnicko ime
	 * @param password lozinka
	 * @param server ime servera
	 * @return <code>true</code> ako se korisnik uspjesno ulogirao, <code>false</code> inace
	 * @throws XMPPException
	 */
	public static boolean login(String username, String password, String server) throws XMPPException {
		if (username.isEmpty() || server.isEmpty()) return false;
		connection = new XMPPConnection(server);
		connection.connect();

		FileTransferNegotiator.setServiceEnabled(connection,true);

		try {
			connection.login(username, password);
			Thread.sleep(3000);
			ftm = new FileTransferManager(connection);
		} catch (Exception e) {
			connection.disconnect();
		}
		return connection.isAuthenticated();
	}

	/**
	 * Metoda za registraciju novog korisnika
	 * @param user podaci o korisniku
	 * @param server ime servera
	 * @return  <code>true</code> ako se korisnik uspjesno registrirao, <code>false</code> inace
	 * @throws XMPPException
	 */
	public static boolean registerNewUser(User user, String server) throws XMPPException {
		XMPPConnection connection = new XMPPConnection(server);
		connection.connect();

		boolean successfulRegistraion = false;

		try {
			connection.getAccountManager().createAccount(user.getUsername(), user.getPassword());
			successfulRegistraion = true;
		} finally {
			connection.disconnect();
		}

		return successfulRegistraion;
	}

	/**
	 * Metoda za prekid veze sa posluziteljem
	 */
	public static void disconnect() {		
		if (connection != null && connection.isConnected())
			connection.disconnect();
	}

	/**
	 * Metoda za postavljanje slusatelja prisutnosti<br>
	 * Postavlja naredbe koje se izvrsavaju ukoliko prijatelj dodje<br>
	 * online ili otidje offline, ako je korisnik dodan u listu<br>
	 * prijatelja drugog korisnika
	 */
	public static void setRosterListener(final MainAppPanel panel) {
		connection.getRoster().addRosterListener(new RosterListener() {

			@Override
			public void presenceChanged(Presence presence) {
				DefaultListModel model = new DefaultListModel();
				for (String entry: getOnlineUsers()) {
					model.addElement(entry);
				}
				panel.getBuddyList().setModel(model);
				panel.getBuddyList().validate();
			}

			@Override
			public void entriesUpdated(Collection<String> addresses) {
			}

			@Override
			public void entriesDeleted(Collection<String> addresses) {
			}

			@Override
			public void entriesAdded(Collection<String> addresses) {

			}
		});
		connection.getRoster().setSubscriptionMode(SubscriptionMode.manual);
		connection.addPacketListener(new PacketListener() {

			@Override
			public void processPacket(Packet packet) {
				Presence userPresence = (Presence) packet;
				if (panel.acceptRequest(userPresence.getFrom())) {
					try {
						Presence subscribed = new Presence(Presence.Type.subscribed);
						subscribed.setTo(userPresence.getFrom());
						connection.sendPacket(subscribed);
						if (!connection.getRoster().contains(userPresence.getFrom())) {
							connection.getRoster().createEntry(userPresence.getFrom(), userPresence.getFrom(), new String[]{"Friends"});
						}
					} catch (XMPPException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}, new PacketFilter() {

			@Override
			public boolean accept(Packet packet) {
				if(packet instanceof Presence) {
					Presence presencePacket = (Presence) packet;
					if((presencePacket).getType().equals(Presence.Type.subscribe))
						return true;
				}
				return false;
			}
		});
		
		connection.addPacketListener(new PacketListener() {
			
			@Override
			public void processPacket(Packet packet) {
				Presence userPresence = (Presence) packet;
				Presence unsubscribed = new Presence(Presence.Type.unsubscribed);
				unsubscribed.setTo(userPresence.getFrom());
				connection.sendPacket(unsubscribed);
				if (connection.getRoster().contains(userPresence.getFrom())) {
					try {
						connection.getRoster().removeEntry(connection.getRoster().getEntry(userPresence.getFrom()));
					} catch (XMPPException e) {
						e.printStackTrace();
					}
				}
			}
		}, new PacketFilter() {
			
			@Override
			public boolean accept(Packet packet) {
				if(packet instanceof Presence) {
					Presence presencePacket = (Presence) packet;
					if((presencePacket).getType().equals(Presence.Type.unsubscribe))
						return true;
				}
				return false;
			}
		});
	}

	/**
	 * Metoda za dohvacanje online prijatelja
	 * @return popis online prijatelja
	 */
	public static Vector<String> getOnlineUsers() {
		Roster roster = connection.getRoster();
		Vector<String> onlineUsers = new Vector<String>();
		for (RosterEntry entry: roster.getEntries()) {
			if (roster.getPresence(entry.getUser()).isAvailable()) {
				onlineUsers.add(roster.getPresence(entry.getUser()).getFrom());
			}
		}
		return onlineUsers;
	}

	/**
	 * Metoda za dohvacanje dostupnih soba
	 * @return popis dostupnih soba
	 */
	public static Vector<HostedRoomPresentation> getAvailableRooms() {
		Vector<HostedRoomPresentation> availableRooms = new Vector<HostedRoomPresentation>();
		HostedRoomPresentation hrp;
		Item item;
		RoomInfo ri;
		try {
			for (HostedRoom room: MultiUserChat.getHostedRooms(connection, "conference." + connection.getServiceName())) {
				ri = MultiUserChat.getRoomInfo(connection, room.getJid());
				item = new Item(room.getJid());
				item.setName(room.getName());
				hrp = new HostedRoomPresentation(item);
				hrp.setRoomInfo(ri);
				availableRooms.add(hrp);
			}
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		return availableRooms;
	}

	/**
	 * Metoda za kreiranje nove individualne komunikacije<br>
	 * izmedju dva korisnika
	 * @param user korisnicko ime prijatelja
	 */
	public static Chat createChat(String user, final IndividualChatWindow chatWindow) {
		return connection.getChatManager().createChat(StringUtils.parseBareAddress(user), new MessageListener() {

			@Override
			public void processMessage(Chat chat, Message message) {
				chatWindow.getTextMessagePane().setText(chatWindow.getTextMessagePane().getText() + StringUtils.parseName(chat.getParticipant()) + ": " + System.getProperty("line.separator") + message.getBody() + System.getProperty("line.separator"));
			}
		});
	}

	/**
	 * Metoda za kreiranje nove sobne komunikacije izabrane iz liste<br>
	 * dostuonih soba te postavljanje slusaca za dolazne poruke<br>
	 * i pracenje korisnika u sobi
	 * @param room ime sobe
	 */
	public static MultiUserChat createMUC(HostedRoomPresentation room, final RoomChatWindow roomWindow) {
		final MultiUserChat muc = new MultiUserChat(connection, room.getJid());
		muc.addMessageListener(new PacketListener() {

			@Override
			public void processPacket(Packet packet) {
				String message = ((Message) packet).getBody();
				roomWindow.getTextMessagePane().setText(roomWindow.getTextMessagePane().getText() + StringUtils.parseResource(packet.getFrom()) + ": " + message + System.getProperty("line.separator"));
			}
		});
		muc.addParticipantListener(new PacketListener() {

			@Override
			public void processPacket(Packet packet) {
				Presence presence = (Presence) packet;
				DefaultListModel userListModel = (DefaultListModel) roomWindow.getUserList().getModel();

				if (presence.isAvailable()) {
					userListModel.addElement(StringUtils.parseResource(presence.getFrom()));
				} else {
					userListModel.removeElement(StringUtils.parseResource(presence.getFrom()));
				}

				roomWindow.getUserList().setModel(userListModel);
			}
		});
		try {
			muc.join(StringUtils.parseName(connection.getUser()));
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		return muc;
	}

	/**
	 * Metoda za postavljanje slusaca za nove individualne komunikacije<br>
	 * Slusac se aktivira ukoliko drugi korisnik<br>
	 * stvori komunikaciju i posalje poruku
	 */
	public static void setChatListener(final MainAppPanel panel) {
		if (connection.isAuthenticated()) {
			connection.getChatManager().addChatListener(new ChatManagerListener() {

				@Override
				public void chatCreated(Chat chat, boolean createdLocally) {
					if (!createdLocally) {
						panel.createNewChatWindow(chat);
					}
				}
			});
		}
	}

	/**
	 * Metoda za slanje datoteke prijatelju
	 * @param user korisnicko ime prijatelja
	 * @param file datoteka koja se salje
	 * @param message poruka uz datoteku
	 */
	public static void sendFile(String user, File file, String message) {
		OutgoingFileTransfer transfer = ftm.createOutgoingFileTransfer(user);
		try {
			transfer.sendFile(file, message);
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Metoda za postavljanje slusaca za dolazece datoteke
	 */
	public static void setFileTransferListener(final MainAppPanel panel) {
		ftm.addFileTransferListener(new FileTransferListener() {

			@Override
			public void fileTransferRequest(FileTransferRequest request) {
				String user = request.getRequestor();
				if (chatWindows.containsKey(user)) {
					processRequest(request, user);
				}
				else {
					panel.createNewChatWindow(user);
					processRequest(request, user);
				}
			}

			private void processRequest(FileTransferRequest request, String user) {
				IndividualChatWindow chatWindow = chatWindows.get(user);
				if (chatWindow.acceptFile(user)) {
					File file = chatWindow.selectFileDestination(request.getFileName());
					if (file != null) {
						IncomingFileTransfer ift = request.accept();
						try {
							ift.recieveFile(file);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
					else {
						request.reject();
					}
				}
				else {
					request.reject();
				}
				String text = chatWindow.getTextMessagePane().getText();
				String description = request.getDescription();
				if (description == null) {
					description = "";
				}
				chatWindow.getTextMessagePane().setText(text + StringUtils.parseName(request.getRequestor()) + ": " + System.getProperty("line.separator") + request.getFileName() + System.getProperty("line.separator") + description + System.getProperty("line.separator"));
			}
		});
	}

	/**
	 * Metoda za kreiranje nove sobe te komunikacije prema njoj<br>
	 * @param room ime sobe
	 */
	public static void createMUC(String name) {
		try {
			MultiUserChat muc = new MultiUserChat(connection, name + "@" + MultiUserChat.getServiceNames(connection).iterator().next());
			muc.create(StringUtils.parseName(connection.getUser()));
			muc.sendConfigurationForm(new Form(Form.TYPE_SUBMIT));
			RoomChatWindow rcw = new RoomChatWindow(muc);

			DefaultListModel dlm = (DefaultListModel) rcw.getUserList().getModel();
			dlm.addElement(StringUtils.parseName(connection.getUser()));
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Metoda za slanje zahtjeva za prijateljstvo
	 * @param jid korisnicko ime prijatelja kojem se salje zahtjev
	 */
	public static void sendContactRequest(String jid) {
		try {
			connection.getRoster().createEntry(jid, jid, new String[]{"Friends"});
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Metoda za brisanje kontakta iz liste prijatelja
	 * @param user korisnicko ime kontakta koji se brise
	 */
	public static void deleteContact(String user) {
		try {
			connection.getRoster().removeEntry(connection.getRoster().getEntry(StringUtils.parseBareAddress(user)));
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}
}
